/*
 * CodeViewer.java  0.4.0 / Oct 24, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.apps.codebrowser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import com.cute4j.swing.Frame;
import com.cute4j.swing.SplitPane;
import com.cute4j.swing.StatusBar;
import com.cute4j.swing.Swing;
import com.cute4j.swing.Tree;
import com.cute4j.swing.events.TreeSelectionEvent;
import com.cute4j.swing.events.TreeSelectionListener;
import com.cute4j.swing.graphics.Font;
import com.cute4j.swing.text.PlainDocument;
import com.cute4j.swing.tree.DefaultMutableTreeNode;
import com.cute4j.swing.tree.DefaultTreeModel;
import com.cute4j.swingx.text.code.CodeViewer;
import com.cute4j.swingx.text.code.CppDocument;
import com.cute4j.swingx.text.code.JavaDocument;

/**
 * Simple Code Viewer.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public final class CodeBrowser extends Frame {

// Private Static Fields. ------------------------------------------------------

  private static final Set TEXT_FILES;
  private static final Set CPP_FILES;

// Static Initialization. ------------------------------------------------------

  static {
    TEXT_FILES = new HashSet(Arrays.asList(new String[] {
        ".txt",
        ".css",
        ".pro",
        ".pri",
        ".qrc",
        ".flex",
        ".mf",
        ".xml"
    }));
    CPP_FILES = new HashSet(Arrays.asList(new String[] {
        ".cpp",
        ".c",
        ".h"
    }));
  }

// Private Fields. -------------------------------------------------------------

  private final StatusBar statusBar;
  private final CodeViewer codeViewer;
  private final PlainDocument plainDocument;
  private CppDocument cppDocument; // NOPMD
  private JavaDocument javaDocument; // NOPMD

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new CodeViewer object.
   */
  public CodeBrowser() {
    super("Cute4j Code Browser");
    setDefaultCloseOperation(QUIT_ON_CLOSE);
    setSize(830, 540);
    setInsets(4, 4, 1, 4);
    setLocationRelativeTo(null);
    SplitPane splitPane = new SplitPane();
    codeViewer = new CodeViewer();
    plainDocument = new PlainDocument();
    splitPane.setRightComponent(codeViewer);
    Tree sourceTree = new Tree(buildTreeModel());
    sourceTree.setFont(sourceTree.getFont().deriveFont(Font.BOLD));
    sourceTree.addChangeListener(new TreeSelectionListener() {
      public void valueChanged(TreeSelectionEvent event) {
        Object selectedNode = event.getPath().getLastPathComponent();
        if (selectedNode instanceof CodeNode) {
          CodeNode sourceNode = (CodeNode)selectedNode;
          if (sourceNode.isLeaf()) {
            String fileName = sourceNode.getSourceFile().getAbsolutePath();
            String fileExtension = fileName.substring(
                                                    fileName.lastIndexOf('.'));
            fileExtension = fileExtension.toLowerCase(); // NOPMD
            if (".java".equals(fileExtension)) {
              loadJavaFile(fileName);
            } else if (CPP_FILES.contains(fileExtension)) {
              loadCppFile(fileName);
            } else if (TEXT_FILES.contains(fileExtension)) { // NOPMD
              loadTextFile(fileName);
            }
          }
        }
      }
    });
    splitPane.setLeftComponent(sourceTree);
    add(splitPane);
    splitPane.setResizeWeight(0.28);
    statusBar = setStatusBar(new StatusBar());
  }

// Private Methods. ------------------------------------------------------------

  private DefaultTreeModel buildTreeModel() {
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("Cute4j Code");
    root.add(new CodeNode("C/C++", "../src/native"));
    root.add(new CodeNode("Java", "../src/java"));
//    root.add(new CodeNode("C/C++", "../../../../../src/native")); // TODO: temp - finish
//    root.add(new CodeNode("Java", "../../../../../src/java"));
    return new DefaultTreeModel(root);
  }

  private void setPlainMessage(String text) {
    codeViewer.setDocument(plainDocument);
    plainDocument.setText(text);
    Swing.flushEvents();
  }

  private void loadTextFile(String fileName) {
    setPlainMessage("Loading...");
    try {
      File file = new File(fileName);
      plainDocument.readFrom(new FileReader(file));
      statusBar.showMessage(file.getCanonicalPath());
    } catch (IOException ioe) {
      setPlainMessage("Loading failed");
    }
  }

  private void loadCppFile(String fileName) {
    statusBar.clearMessage();
    setPlainMessage("Loading...");
    if (cppDocument == null) {
      cppDocument = new CppDocument();
    }
    try {
      File file = new File(fileName);
      cppDocument.readFrom(new FileReader(file));
      codeViewer.setDocument(cppDocument);
      statusBar.showMessage(file.getCanonicalPath());
    } catch (IOException ioe) {
      setPlainMessage("Loading failed");
    }
  }

  private void loadJavaFile(String fileName) {
    statusBar.clearMessage();
    setPlainMessage("Loading...");
    if (javaDocument == null) {
      javaDocument = new JavaDocument();
    }
    try {
      File file = new File(fileName);
      javaDocument.readFrom(new InputStreamReader(new FileInputStream(file),
                                                  "UTF8"));
      codeViewer.setDocument(javaDocument);
      statusBar.showMessage(file.getCanonicalPath());
    } catch (IOException ioe) {
      setPlainMessage("Loading failed");
    }
  }

// Main Method. ----------------------------------------------------------------

  /**
   * Starts CodeViewer.
   *
   * @param args the application arguments
   */
  public static void main(String[] args) {
    Swing.initialize(args);
    CodeBrowser codeViewer = new CodeBrowser();
    codeViewer.setVisible(true);
    Swing.startEventLoop();
  }

}
